module decode(
    input clk,
    input reset,

    input [31:0] inst,
    input [63:0] inst_addr,

    input [63:0] rdata1,
    input [63:0] rdata2,

    input [4:0] exe_out_addr,
    input [63:0] exe_out,
    input [4:0] mem_out_addr,
    input [63:0] mem_out_1,
    input [63:0] mem_out_2,
    input exe_write_to_regfile,
    input mem_write_to_regfile,
    input exe_mem_to_regfile,
    input mem_mem_to_regfile,
    input [63:0] i_excode,
    input i_except_ena,
    //input wb_write_to_regfile,

    output write_to_mem,
    output write_to_regfile,
    output [4:0] reg_addr,
    output [16:0] ALUControl,
    output [63:0] b_j_pc,
    output pcsource,
    output [63:0] sext_imm,
    output aluimm,
    output [63:0] data1,
    output [63:0] data2,
    output is_jump,
    output [3:0] mem_control,
    output mem_to_regfile,
    output [11:0] csr,
    output [63:0] csr_wdata,
    output [2:0] csr_control,
    output [63:0] o_excode,
    output o_except_ena,
    output flush_if_id,
    output ret,
    output shift,
    output [63:0] shamt_ext
);
////////////////////////////////////////////////////////U指令
    wire inst_lui;
    wire inst_auipc;
////////////////////////////////////////////////////////
    wire inst_jal;
    wire inst_jalr;
////////////////////////////////////////////////////////B指令
    wire inst_beq;
    wire inst_bne;
    wire inst_blt;
    wire inst_bge;
    wire inst_bltu;
    wire inst_bgeu;
////////////////////////////////////////////////////////I指令
    wire inst_lb;
    wire inst_lh;
    wire inst_lw;
    wire inst_lbu;
    wire inst_lhu;
    wire inst_lwu;
    wire inst_ld;

    wire inst_addi;
    wire inst_addiw;
    wire inst_slti;
    wire inst_sltiu;
    wire inst_xori;
    wire inst_ori;
    wire inst_andi;
    wire inst_slli;
    wire inst_srli;
    wire inst_srai;

    wire inst_fence;
    wire inst_fence_i;
    wire inst_ecall;
    wire inst_ebreak;
    wire inst_csrrw;
    wire inst_csrrs;
    wire inst_csrrc;
    wire inst_csrrwi;
    wire inst_csrrsi;
    wire inst_csrrci;

    wire inst_sret;
    wire inst_mret;
    wire inst_wfi;
    wire inst_sfence_vma;
////////////////////////////////////////////////////////S指令
    wire inst_sb;
    wire inst_sh;
    wire inst_sw;
    wire inst_sd;
////////////////////////////////////////////////////////R指令
    wire inst_add;
    wire inst_addw;
    wire inst_sub;
    wire inst_subw;
    wire inst_sll;
    wire inst_slt;
    wire inst_sltu;
    wire inst_sllw;
    wire inst_slliw;
    wire inst_xor;
    wire inst_srl;
    wire inst_srlw;
    wire inst_srliw;
    wire inst_sra;
    wire inst_sraw;
    wire inst_sraiw;
    wire inst_or;
    wire inst_and;
////////////////////////////////////////////////////////
    wire shift_op;
    wire arithmetic_op;
    wire logic_op;
    wire compare_op;
    wire branch_op;
    wire synch_op;
    wire jumplink_op;
    wire environment_op;
    wire CSR_op;
    wire load_op;
    wire store_op;
////////////////////////////////////////////////////////
    wire [6:0] op;
    wire [4:0] rd;
    wire [4:0] rs1;
    wire [4:0] rs2;
    wire [2:0] func_3;
    wire [6:0] func_7;
    wire [5:0] func_6;
    wire [11:0] imm_11_0;
    wire [11:0] imm_11_5_4_0;

    assign op = inst[6:0];
    assign rd = inst[11:7];
    assign rs1 = inst[19:15];
    assign rs2 = inst[24:20];
    assign func_3 = inst[14:12];
    assign func_7 = inst[31:25];
    assign func_6 = inst[31:26];
    assign imm_11_0 = inst[31:20];
    assign imm_11_5_4_0 = {inst[31:25], inst[11:7]};
    assign csr = inst[31:20];
////////////////////////////////////////////////////////
    wire inst_decode;

    assign inst_decode = 
     inst_lui   | inst_auipc   |
     inst_jal   | inst_jalr    |
     inst_beq   | inst_bne     | inst_blt   | inst_bge    | inst_bltu  |  inst_bgeu |
     inst_lb    | inst_lh      | inst_lw    | inst_lbu    | inst_lhu   |  inst_lwu  | inst_ld    |
     inst_sb    | inst_sh      | inst_sw    | inst_sd     |
     inst_addi  | inst_slti    | inst_sltiu | inst_xori   | inst_ori   | inst_andi  | inst_slli  | inst_srli   | inst_srai    | inst_addiw  |
     inst_add   | inst_sub     | inst_sll   | inst_slt    | inst_sltu  | inst_xor   | inst_srl   | inst_sra    | inst_or      | inst_and    | inst_sllw | inst_slliw | inst_srlw | inst_srliw | inst_sraw | inst_sraiw | inst_addw | inst_subw |
     inst_fence | inst_fence_i | inst_ecall | inst_ebreak | inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi  | inst_csrrci |
     inst_sret  | inst_mret    | inst_wfi   | inst_sfence_vma;

    assign inst_lui = op == 7'b0110111;
    assign inst_auipc = op == 7'b0010111;

    assign inst_jal = op == 7'b1101111;
    assign inst_jalr = op == 7'b1100111;

    assign inst_beq = op == 7'b1100011 && func_3 == 3'b000;
    assign inst_bne = op == 7'b1100011 && func_3 == 3'b001;
    assign inst_blt = op == 7'b1100011 && func_3 == 3'b100;
    assign inst_bge = op == 7'b1100011 && func_3 == 3'b101;
    assign inst_bltu = op == 7'b1100011 && func_3 == 3'b110;
    assign inst_bgeu = op == 7'b1100011 && func_3 == 3'b111;

    assign inst_lb = op == 7'b0000011 && func_3 == 3'b000;
    assign inst_lh = op == 7'b0000011 && func_3 == 3'b001;
    assign inst_lw = op == 7'b0000011 && func_3 == 3'b010;
    assign inst_lbu = op == 7'b0000011 && func_3 == 3'b100;
    assign inst_lhu = op == 7'b0000011 && func_3 == 3'b101;
    assign inst_lwu = op == 7'b0000011 && func_3 == 3'b110;
    assign inst_ld = op == 7'b0000011 && func_3 == 3'b011;

    assign inst_sb = op == 7'b0100011 && func_3 == 3'b000;
    assign inst_sh = op == 7'b0100011 && func_3 == 3'b001;
    assign inst_sw = op == 7'b0100011 && func_3 == 3'b010;
    assign inst_sd = op == 7'b0100011 && func_3 == 3'b011;

    assign inst_addi = op == 7'b0010011 && func_3 == 3'b000;
    assign inst_addiw = op == 7'b0011011 && func_3 == 3'b000;
    assign inst_slti = op == 7'b0010011 && func_3 == 3'b010;
    assign inst_sltiu = op == 7'b0010011 && func_3 == 3'b011;
    assign inst_xori = op == 7'b0010011 && func_3 == 3'b100;
    assign inst_ori = op == 7'b0010011 && func_3 == 3'b110;
    assign inst_andi = op == 7'b0010011 && func_3 == 3'b111;
    assign inst_slli = op == 7'b0010011 && func_3 == 3'b001 && func_6 == 6'b000000;
    assign inst_slliw = op == 7'b0011011 && func_3 == 3'b001 && func_6 == 6'b000000;
    assign inst_srli = op == 7'b0010011 && func_3 == 3'b101 && func_6 == 6'b000000;
    assign inst_srliw = op == 7'b0011011 && func_3 == 3'b101 && func_6 == 6'b000000;
    assign inst_srai = op == 7'b0010011 && func_3 == 3'b101 && func_6 == 6'b010000;
    assign inst_sraiw = op == 7'b0011011 && func_3 == 3'b101 && func_6 == 6'b010000;
    
    assign inst_add = op == 7'b0110011 && func_3 == 3'b000 && func_7 == 7'b0000000;
    assign inst_addw = op == 7'b0111011 && func_3 == 3'b000 && func_7 == 7'b0000000;
    assign inst_sub = op == 7'b0110011 && func_3 == 3'b000 && func_7 == 7'b0100000;
    assign inst_subw = op == 7'b0111011 && func_3 == 3'b000 && func_7 == 7'b0100000;
    assign inst_sll = op == 7'b0110011 && func_3 == 3'b001 && func_7 == 7'b0000000;
    assign inst_sllw = op == 7'b0111011 && func_3 == 3'b001 && func_7 == 7'b0000000;
    assign inst_slt = op == 7'b0110011 && func_3 == 3'b010 && func_7 == 7'b0000000;
    assign inst_sltu = op == 7'b0110011 && func_3 == 3'b011 && func_7 == 7'b0000000;
    assign inst_xor = op == 7'b0110011 && func_3 == 3'b100 && func_7 == 7'b0000000;
    assign inst_srl = op == 7'b0110011 && func_3 == 3'b101 && func_7 == 7'b0000000;
    assign inst_srlw = op == 7'b0111011 && func_3 == 3'b101 && func_7 == 7'b0000000;
    assign inst_sra = op == 7'b0110011 && func_3 == 3'b101 && func_7 == 7'b0100000;
    assign inst_sraw = op == 7'b0111011 && func_3 == 3'b101 && func_7 == 7'b0100000;
    assign inst_or = op == 7'b0110011 && func_3 == 3'b110 && func_7 == 7'b0000000;
    assign inst_and = op == 7'b0110011 && func_3 == 3'b111 && func_7 == 7'b0000000;

    assign inst_fence = op == 7'b0001111 && func_3 == 3'b000;
    assign inst_fence_i = op == 7'b0001111 && func_3 == 3'b001;

    assign inst_ecall = op == 7'b1110011 && func_3 == 3'b000 && imm_11_0 == 12'b000000000000;
    assign inst_ebreak = op == 7'b1110011 && func_3 == 3'b000 && imm_11_0 == 12'b000000000001;

    assign inst_csrrw = op == 7'b1110011 && func_3 == 3'b001;
    assign inst_csrrs = op == 7'b1110011 && func_3 == 3'b010;
    assign inst_csrrc = op == 7'b1110011 && func_3 == 3'b011;
    assign inst_csrrwi = op == 7'b1110011 && func_3 == 3'b101;
    assign inst_csrrsi = op == 7'b1110011 && func_3 == 3'b110;
    assign inst_csrrci = op == 7'b1110011 && func_3 == 3'b111;

    assign inst_sret = op == 7'b1110011 && func_7 == 7'b0001000 && rs2 == 5'b00010;
    assign inst_mret = op == 7'b1110011 && func_7 == 7'b0011000 && rs2 == 5'b00010;
    assign inst_wfi = op == 7'b1110011 && func_7 == 7'b0001000 && rs2 == 5'b00101;
    assign inst_sfence_vma = op == 7'b1110011 && func_7 == 7'b0001001;

    assign shift_op       = inst_sll   | inst_slli  | inst_srl   | inst_srli   | inst_sra    | inst_srai | inst_sllw | inst_slliw | inst_srlw | inst_srliw | inst_sraw | inst_sraiw;
    assign arithmetic_op  = inst_add   | inst_addi  | inst_sub   | inst_lui    | inst_auipc  | inst_addw | inst_subw | inst_addiw;
    assign logic_op       = inst_xor   | inst_xori  | inst_or    | inst_ori    | inst_and    | inst_andi;
    assign compare_op     = inst_slt   | inst_slti  | inst_sltu  | inst_sltiu;
    assign branch_op      = inst_beq   | inst_bne   | inst_blt   | inst_bge    | inst_bltu   | inst_bgeu;
    assign jumplink_op    = inst_jal   | inst_jalr;
    assign synch_op       = inst_fence | inst_fence_i;
    assign environment_op = inst_ecall | inst_ebreak;
    assign CSR_op         = inst_csrrw | inst_csrrs | inst_csrrc | inst_csrrwi | inst_csrrsi | inst_csrrci;
    assign load_op        = inst_lb    | inst_lh    | inst_lbu   | inst_lhu    | inst_lw     | inst_lwu  | inst_ld;
    assign store_op       = inst_sb    | inst_sh    | inst_sw    | inst_sd;
////////////////////////////////////////////////////////
    assign write_to_mem     = inst_sb    | inst_sh     | inst_sw   | inst_sd;
    assign write_to_regfile = inst_sll   | inst_slli   | inst_srl  | inst_srli  | inst_srai | inst_add   | inst_addi   | inst_sub   | inst_lui   |
                              inst_auipc | inst_xor    | inst_xori | inst_or    | inst_ori  | inst_and   | inst_andi   | inst_slt   | inst_slti  |
                              inst_sltu  | inst_sltiu  | inst_jalr | inst_lb    | inst_lh   | inst_lbu   | inst_lw     | inst_sllw  | inst_srlw  |
                              inst_slliw | inst_srliw  | inst_sraw | inst_sraiw | inst_addw | inst_addiw | inst_subw   | inst_lwu   | inst_ld    |
                              inst_csrrw | inst_csrrwi | inst_jal  | inst_lhu   | inst_sra  | inst_csrrc | inst_csrrci | inst_csrrs | inst_csrrsi;
    assign reg_addr = rd;
////////////////////////////////////////////////////////控制寄存器堆
    //wire [63:0] rdata1;
    //wire [63:0] rdata2;

    //regfile Regfile(.clk(clk), .reset(reset), .en(wb_write_to_regfile), .raddr1(inst[19:15]), .raddr2(inst[24:20]), .waddr(waddr), .wdata(wdata), .rdata1(rdata1), .rdata2(rdata2));


////////////////////////////////////////////////////////
    assign ALUControl = {
        inst_addi | inst_add  | inst_sb | inst_sh | inst_sw | inst_sd | inst_lb | inst_lh | inst_lbu | inst_lhu | inst_lw | inst_lwu | inst_ld,
        inst_addw | inst_addiw ,
        inst_sub,
        inst_subw,
        inst_lui,
        inst_auipc,
        inst_xor  | inst_xori,
        inst_or   | inst_ori,
        inst_and  | inst_andi,
        inst_sltu | inst_sltiu,
        inst_sll  | inst_slli,
        inst_sllw | inst_slliw,
        inst_srl  | inst_srli,
        inst_srlw | inst_srliw,
        inst_sra  | inst_srai,
        inst_sraw | inst_sraiw,
        inst_slt  | inst_slti
    };
////////////////////////////////////////////////////////
    wire is_branch = inst_beq  & (data1 == data2) |
                     inst_bne  & (data1 != data2) |
                     inst_blt  & (data1[63] == 0 ? (data2[63] == 1 ? 0 : data1 < data2) : (data2[63] == 0 ? 1 : data1 < data2)) |
                     inst_bge  & (data1[63] == 0 ? (data2[63] == 1 ? 1 : data1 >= data2) : (data2[63] == 0 ? 0 : data1 >= data2)) |
                     inst_bltu & (data1 <  data2) |
                     inst_bgeu & (data1 >= data2);
    assign is_jump = inst_jalr | inst_jal;

    wire [63:0] jump_pc;
    wire [63:0] branch_pc;
    wire [63:0] jalr_pc;
    
    assign jalr_pc = data1 + {{52{inst[31]}}, inst[31:20]};

    assign jump_pc = inst_jal ? inst_addr + {{(43){inst[31]}}, inst[31], inst[19:12], inst[20], inst[30:21], 1'b0} : {jalr_pc[63:1], 1'b0};
    assign branch_pc = inst_addr + {{(51){inst[31]}}, inst[31], inst[7], inst[30:25], inst[11:8], 1'b0};

    assign shift = inst_slli | inst_slliw | inst_srli | inst_srliw | inst_srai | inst_sraiw;
    assign shamt_ext = {58'd0, inst[25:20]};
    
    assign pcsource = is_jump | (is_branch & branch_op);
    assign b_j_pc = is_jump ? jump_pc : (is_branch & branch_op ? branch_pc : 64'd0);

    assign sext_imm = inst_auipc ? {{32{inst[31]}}, inst[31:12], 12'd0} : (inst_sb | inst_sh | inst_sw | inst_sd) ? {{52{imm_11_5_4_0[11]}}, imm_11_5_4_0[11:0]} : (inst_lui ? {{44{inst[31]}}, inst[31:12]} : {{53{imm_11_0[11]}}, imm_11_0[10:0]});
    assign aluimm = inst_lui | inst_auipc | inst_addi | inst_slti | inst_sltiu | inst_xori | inst_ori | inst_andi | inst_addiw | inst_lb | inst_lh | inst_lbu | inst_lhu | inst_lw | inst_lwu | inst_ld | inst_sb | inst_sh | inst_sw | inst_sd;

    assign mem_to_regfile = load_op;
    assign mem_control = inst_lb ? 4'b0001 : (inst_lh ? 4'b0010 : (inst_lbu ? 4'b0011 : (inst_lhu ? 4'b0100 : (inst_lw ? 4'b0101 : (inst_lwu ? 4'b0110 : (inst_ld ? 4'b0111 : (inst_sb ? 4'b1000 : (inst_sh ? 4'b1001 : (inst_sw ? 4'b1010 : (inst_sd ? 4'b1011 : 4'b0000))))))))));

    assign csr_wdata = (inst_csrrci | inst_csrrwi | inst_csrrsi) ? {59'd0, rs1} : rdata1;
    assign csr_control = {
        inst_csrrw | inst_csrrwi,
        inst_csrrs | inst_csrrsi,
        inst_csrrc | inst_csrrci
    };

    assign o_except_ena = i_except_ena ? 1'b1 : inst_ebreak | inst_ecall;
    assign o_excode = i_except_ena ? i_excode : (~inst_decode ? {1'b0, 63'd2} : (inst_ebreak ? {1'b0, 63'd3} : (inst_ecall ? {1'b0, 63'd11} : 64'd0)));

    assign flush_if_id = is_branch | is_jump;
    assign ret = inst_mret;
////////////////////////////////////////////////////////
    reg [1:0] fwda;
    reg [1:0] fwdb;

    always@(*) begin
        fwda = 2'b00;
        if(exe_write_to_regfile && (exe_out_addr != 0) && (rs1 == exe_out_addr) && ~exe_mem_to_regfile) begin
            fwda = 2'b01;
        end else begin
            if(mem_write_to_regfile && (mem_out_addr != 0) && (mem_out_addr == rs1) && ~mem_mem_to_regfile) begin
                fwda = 2'b10;
            end else begin
                if(mem_write_to_regfile && (mem_out_addr != 0) && (mem_out_addr == rs1) && mem_mem_to_regfile) begin
                    fwda = 2'b11;
                end
            end
        end
    end

    always@(*) begin
        fwdb = 2'b00;
        if(exe_write_to_regfile && (exe_out_addr != 0) && (rs2 == exe_out_addr) && ~exe_mem_to_regfile) begin
            fwdb = 2'b01;
        end else begin
            if(mem_write_to_regfile && (mem_out_addr != 0) && (mem_out_addr == rs2) && ~mem_mem_to_regfile) begin
                fwdb = 2'b10;
            end else begin
                if(mem_write_to_regfile && (mem_out_addr != 0) && (mem_out_addr == rs2) && mem_mem_to_regfile) begin
                    fwdb = 2'b11;
                end
            end
        end
    end

    mux4x64 mux1(.control(fwda), .a0(rdata1), .a1(exe_out), .a2(mem_out_1), .a3(mem_out_2), .out(data1));
    mux4x64 mux2(.control(fwdb), .a0(rdata2), .a1(exe_out), .a2(mem_out_1), .a3(mem_out_2), .out(data2));
////////////////////////////////////////////////////////
endmodule
